const http = require("http");
const path = require("path");
const fse = require("fs-extra");
const multiparty = require("multiparty");

const server = http.createServer();
const UPLOAD_DIR = path.resolve(__dirname, "..", "target"); // 大文件存储目录
const extractExt = filename =>
    filename.slice(filename.lastIndexOf("."), filename.length); // 提取后缀名

const resolvePost = req =>
    new Promise(resolve => {
        let chunk = "";
        req.on("data", data => {
            chunk += data;
        });
        req.on("end", () => {
            resolve(JSON.parse(chunk));
        });
    });

// 返回已经上传切片名列表
const createUploadedList = async fileHash =>
    fse.existsSync(path.resolve(UPLOAD_DIR, fileHash)) ?
    await fse.readdir(path.resolve(UPLOAD_DIR, fileHash)) : [];


const pipeStream = (path, writeStream) => {

    try {
        return new Promise(resolve => {
            const readStream = fse.createReadStream(path);
            readStream.on("end", () => {
                fse.unlinkSync(path);
                resolve();
            });
            readStream.pipe(writeStream);
        });
    } catch (e) {
        console.log(e, 'pipeStream')
    }
}

// 合并切片
const mergeFileChunk = async (filePath, filename, size) => {

    const chunkDir = path.resolve(UPLOAD_DIR, filename);
    const chunkPaths = await fse.readdir(chunkDir);
    // return chunkPaths
    // 根据切片下标进行排序
    // 否则直接读取目录的获得的顺序可能会错乱
    try {
        chunkPaths.sort((a, b) => {
            let a1 = a.split("-")
            let b1 = b.split("-")
            return a1[a1.length - 1] - b1[b1.length - 1]
            // a.split("-")[1] - b.split("-")[1]
        });
        await Promise.all(
            chunkPaths.map((chunkPath, index) => {

                let params = {
                    start: index * size,
                    end: (index + 1) * size
                }
                return pipeStream(
                    path.resolve(chunkDir, chunkPath),
                    // 指定位置创建可写流
                    fse.createWriteStream(filePath, params)
                )
            })
        );
    } catch (e) {
        console.log(e)
    }
    fse.rmdirSync(chunkDir); // 合并后删除保存切片的目录
};



server.on("request", async (req, res) => {
    res.setHeader("Access-Control-Allow-Origin", "*");
    res.setHeader("Access-Control-Allow-Headers", "*");
    if (req.method === "OPTIONS") {
        res.status = 200;
        res.end();
        return;
    }
    if (req.url === "/merge") {
        const data = await resolvePost(req);
        const {
            filename,
            size,
            suffix
        } = data;
        let newFileName = filename + suffix
        // .../target/success,必须先建立success文件夹
        const filePath = path.resolve(UPLOAD_DIR, 'success', `${newFileName}`);
        // filename 用户获取切片 hash
        await mergeFileChunk(filePath, filename, size);
        res.end(
            JSON.stringify({
                code: 0,
                message: "file merged success"
            })
        );
    } else if (req.url === '/upload') {
        // 需要做异常处理：前端中断请求，server需要清除相应文件
        const multipart = new multiparty.Form();
        multipart.parse(req, async (err, fields, files) => {
            if (err) {
                return;
            }
            const [chunk] = files.chunk;
            const [hash] = fields.hash;
            const [filename] = fields.filename;
            const chunkDir = path.resolve(UPLOAD_DIR, filename);

            // 切片目录不存在，创建切片目录
            if (!fse.existsSync(chunkDir)) {
                // res.end("file Already exists");
                await fse.mkdirs(chunkDir, 0777);
            }

            // fs-extra 专用方法，类似 fs.rename 并且跨平台
            // fs-extra 的 rename 方法 windows 平台会有权限问题
            // https://github.com/meteor/meteor/issues/7852#issuecomment-255767835
            await fse.move(chunk.path, `${chunkDir}/${hash}`);
            res.end("received file chunk");

        });
    } else if (req.url === "/verify") {
        const data = await resolvePost(req);
        const {
            fileHash,
            filename
        } = data;
        const ext = extractExt(filename);
        const filePath = path.resolve(UPLOAD_DIR, 'success', `${fileHash}${ext}`);
        console.log(filePath, 'filePath')
        if (fse.existsSync(filePath)) {
            res.end(
                JSON.stringify({
                    shouldUpload: false
                })
            );
        } else {
            res.end(
                JSON.stringify({
                    shouldUpload: true,
                    uploadedList: await createUploadedList(fileHash)
                })
            );
        }
    }

});

server.listen(3000, () => console.log("正在监听 3000 端口"));